#include "message_queue.h"

/* initializes m_queue */
void initialize_message_queue(message_queue* m_queue){
  m_queue->queue = NULL;
  m_queue->status = NOT_WAITING;
}

/* frees a message queue and all the messages in it. Returns SUCCESS or FAILURE */
int32_t free_message_queue(message_queue* m_queue){
  list current;
  for(current = m_queue->queue; current != NULL; current = m_queue->queue){
    //let's free this link, the message itself has already been freed by free_all_pid
    if(linked_list_delete_after_prev(&(m_queue->queue), NULL, current, NULL) == FAILURE)
      return FAILURE;
  }
  //if everything went well, the queue should be empty now
  return (m_queue->queue == NULL?SUCCESS:FAILURE);
}

/* insert the message mess in the queue m_queue. Returns SUCCESS in case of success, OUT_OF_MEMORY otherwise */
int32_t insert_message(message_queue* m_queue, message* mess){
  return linked_list_add_tail(&(m_queue->queue),mess);
}

/* returns the first message and removes it from the queue (if any). Returns SUCCESS if a message is found, FAILURE otherwise. */
int32_t get_message_first(message_queue* m_queue, message** mess){
  if(m_queue->queue == NULL)
    return FAILURE; //the queue is empty
  //at this point, the queue can't be empty
  *mess = (message*)m_queue->queue->data;
  return linked_list_delete_after_prev(&(m_queue->queue), NULL, m_queue->queue, NULL);
}

/* returns the first message sent by from and removes it from the queue (if any). Returns SUCCESS if a message is found, FAILURE otherwise.*/
int32_t get_message_from(message_queue* m_queue, int32_t from, message** mess){
  list current, prev = NULL;
  message* m;
  
  for(current = m_queue->queue; current != NULL; prev = current, current = current->next){
    m = (message*)current->data;
    if(message_get_from(m) == from){
      //we found the message we are looking for
      *mess = m;
      return linked_list_delete_after_prev(&(m_queue->queue), prev, current, NULL);
    }
  }
  return FAILURE;
}

/* returns the first message with the correct priority and removes it from the queue (if any). Returns 1 if a message is found, 0 otherwise. */
int32_t get_message_priority(message_queue* m_queue, int32_t priority, message** mess){
  list current, prev = NULL;
  message* m;
  for(current = m_queue->queue; current != NULL; prev = current, current = current->next){
    m = (message*)current->data;
    if(message_get_priority(m) == priority){
      //we found the message we are looking for
      *mess = m;
      return linked_list_delete_after_prev(&(m_queue->queue), prev, current, NULL);
    }
  }
  return FAILURE;  
}

/* sets the status information of the message queue m_queue and blocks the process */
void set_status_information(message_queue* m_queue, int32_t status, int32_t status_data){
  m_queue->status = status;
  m_queue->status_data = status_data;
}

/* checks if the arrival of the message pointed by m should wake the process to up */
int32_t wake_me_up(message_queue* m_queue,message* m){
  int32_t status = m_queue->status;
  if(status != NOT_WAITING){
    //the process needs to be waken up
    int32_t status_data = m_queue->status_data;
    switch(status){
    case WAITING_FIRST:
      //no filtering
      return 1;
    case WAITING_PRIORITY:
      if(message_get_priority(m) == status_data)
	return 1;
      break;
    case WAITING_FROM:
      if(message_get_from(m) == status_data)
	return 1;
      break;
    default:
      ;
    }
  }
  return 0;
}

/* returns the number of messages in this queue */
int32_t message_queue_get_count(message_queue* m_queue){
  return count_list(m_queue->queue);
}
